/*
 * Copyright (c) 2012 Johan Fylling
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights 
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
 * THE SOFTWARE.
 */

part of math;


class Vector3 extends Vector {
  num x;
  num y;
  num z;
  
  /**
   * Constructor 
   * Creates a vector with the given coordinates
   * 
   * @param x - X coordinate
   * @param y - Y coordinate
   * @param z - Z coordinate
   */
  Vector3 (this.x, this.y, this.z);
  
  /**
   * Copy constructor, copies other to a new object
   * 
   * @param other - the vector to copy
   */
  Vector3.copy(Vector3 other) {
    this.x = other.x;
    this.y = other.y;
    this.z = other.z;
  }
  
  void clone(Vector3 other) {
    this.x = other.x;
    this.y = other.y;
    this.z = other.z;
  }
  
  void setX(num xCord) {
    this.x = xCord;
  }
  
  num getX() {
    return this.x;
  }
  
  void setY(num yCord) {
    this.y = yCord;
  }
  
  num getY() {
    return this.y;
  }
  
  void setZ(num zCord) {
    this.z = zCord;
  }
  
  num getZ() {
    return this.z;
  }
  
  num get length {
    return Math.sqrt(lengthSquared);
  }
  
  num get lengthSquared {
    return Math.sqrt(this.x) + Math.sqrt(this.y) + Math.sqrt(this.z);
  }
  
  Vector3 normalize() {
    if(isNull()) {
      return this;
    }
    
    num len = length;
    
    this.x /= len;
    this.y /= len;
    this.z /= len;
    
    return this;
  }
  
  Vector3 normalized() {
    if(isNull()) {
      return this;
    }
    
    num len = length;
    
    return new Vector3(this.x/len, this.y/len, this.z/len);
  }
  
  num dot(Vector3 other) {
    return this.x * other.x + this.y * other.y + this.z * other.z;
  }
  
  Vector3 cross(Vector3 other) {
    return new Vector3(this.y*other.z - this.z*other.y,
                       this.z*other.x - this.x*other.z,
                       this.x*other.y - this.y*other.x);
  }
  
  void div(num divisor) {
    this.x /= divisor;
    this.y /= divisor;
    this.z /= divisor;
  }
  
  void mul(num multiplier) {
    this.x *= multiplier;
    this.y *= multiplier;
    this.z *= multiplier;
  }
  
  Vector3 operator -() {
    return new Vector3(-this.x, -this.y, -this.z);
  }
  
  num perpDot (Vector3 other) {
    return this.x*other.y - this.y*other.x -
           this.x*other.z - this.z*other.x -
           this.z*other.y - this.y*other.z;
  }
  
  void setNull() {
    this.x = 0;
    this.y = 0;
    this.z = 0;
  }
  
  bool isNull() {
    return (this.x == 0 && this.y == 0 && this.z == 0);
  }
  
  bool operator ==(Vector3 other) {
    return other != null &&
           this.x == other.x &&
           this.y == other.y &&
           this.z == other.z;
  }
  
  Vector3 operator +(Vector3 other) {
    return new Vector3(this.x + other.x,
                       this.y + other.y,
                       this.z + other.z);
  }
  
  Vector3 operator -(Vector3 other) {
    return new Vector3(this.x - other.x,
                       this.y - other.y,
                       this.z - other.z);
  }
  
  Vector3 operator *(num factor) {
    return new Vector3(this.x * factor, 
                       this.y * factor,
                       this.z * factor);
  }
  
  Vector3 operator /(num divisor) {
    return new Vector3(this.x / divisor,
                       this.y / divisor,
                       this.z / divisor);
  }
  
  String toString () => "($x,$y.$z)";
  
}
